<scriptlet>
<implements type="Automation" id="dispatcher">
  <property name="PluginEvent">
    <get/>
  </property>
  <property name="PluginDescription">
    <get/>
  </property>
  <property name="PluginFileFilters">
    <get/>
  </property>
  <property name="PluginIsAutomatic">
    <get/>
  </property>
  <property name="PluginUnpackedFileExtension">
    <get/>
  </property>
  <property name="PluginExtendedProperties">
    <get/>
  </property>
  <method name="PluginOnEvent"/>
  <method name="UnpackFile"/>
  <method name="PackFile"/>
  <method name="IsFolder"/>
  <method name="UnpackFolder"/>
  <method name="PackFolder"/>
  <method name="ShowSettingsDialog"/>
</implements>

<script language="JScript">

/*
    This is a plugin for WinMerge.
    It will display the text content of MS Excel files.
    Copyright (C) 2005-2025 Takashi Sawanaka

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


var REGKEY_PATH = "Plugins\\CompareMSExcelFiles.sct/";
var MsgCannotGetMacros = "${Cannot get Macros.\r\n" + 
  "Enable 'Trust access to Visual Basic Project' in MS Office Macro Security.}";
var pbGroup = 6;

var fso = new ActiveXObject("Scripting.FileSystemObject");
var wsh = new ActiveXObject("WScript.Shell");
var mergeApp;

function isAccessibleVBAProject(wbk) {
  try {
    return (wbk.VBProject.VBComponents.Count >= 0);
  } catch (e) {
    return false;
  }
}

function regRead(Key, DefaultValue) {
  return mergeApp.GetOption(Key, DefaultValue);
}

function regWrite(Key, Value, TypeNm) {
  mergeApp.SaveOption(Key, (TypeNm === "REG_DWORD") ? parseInt(Value, 10) : String(Value));
}

function toString(val) {
  switch (typeof val) {
  case "string":
    return val;
  case "number":
    var s = val.toString();
    return (s.length > 16) ? String(parseFloat(val.toPrecision(15))) : s;
  case "date":
    var d = new Date(val);
    if (d.getHours() == 0 && d.getMinutes() == 0 && d.getSeconds() == 0 && d.getMilliseconds() == 0) {
        return d.toLocaleDateString();
    }
    return d.toLocaleString();
  case "undefined":
    return "";
  default:
    return String(val);
  }
}

function writeObjectProperties(fo, items) {
  try {
    for (var it = new Enumerator(items); !it.atEnd(); it.moveNext()) {
      try {
        var o = it.item();
        fo.WriteLine(o.Name + ": " + toString(o.Value));
      } catch (e) {}
    }
  } catch (e) {}
}

function quoteIfNeeded(str) {
  var fQuote;
  if (str.indexOf("\t") >= 0) {
    fQuote = true;
  } else if (str.indexOf("\"") >= 0) {
    fQuote = true;
  } else if (str.indexOf("\n") >= 0) {
    fQuote = true;
  }
  if (fQuote) {
    return "\"" + str.replace(/\"/g, "\"\"") + "\"";
  } else {
    return str;
  }
}

function writeCellValues(fo, sht) {
  var varCells = sht.UsedRange.Value;
  try {
    if (typeof(varCells) === "string" || typeof(varCells) === "number" || typeof(varCells) === "date") {
      fo.WriteLine(quoteIfNeeded(String(varCells)));
    } else if (varCells !== undefined) {
      for (var row = 1; row <= varCells.ubound(1); row++) {
        var ary = new Array(varCells.ubound(2));
        for (var col = 1; col <= varCells.ubound(2); col++) {
          try {
            var val = varCells.getItem(row, col);
            ary[col - 1] = quoteIfNeeded(toString(val));
          } catch (e) {
            ary[col - 1] = "Error" + e.number;
          }
        }
        for (var i = ary.length - 1; i >= 0; i--) {
            if (ary[i] !== "") {
               if (i < ary.length - 1) {
                 ary = ary.splice(0, i + 1);
               }
               break;
            }
        }
        fo.WriteLine(ary.join("\t"));
      }
    }
  } catch (e) {
    fo.WriteLine("Error" + e.number + ": " + e.message);
  }
}

function getAddr(row, col) {
  var addr = "";
  var c = col - 1;
  do {
    addr = String.fromCharCode(65 + (c % 26)) + addr;
    c = Math.floor(c / 26) - 1;
  } while (c >= 0);
  return addr + row;
}

function writeFormulas(fo, sht) {
  var row, col, formula;
  var rowOffset = sht.UsedRange.Row;
  var colOffset = sht.UsedRange.Column;
  var replaceLineBreaks = regRead(REGKEY_PATH + "CompareFormulasReplaceLineBreaks", false);
  try {
    var varCells = sht.UsedRange.Formula;
    if (typeof(varCells) === "string" || typeof(varCells) === "number" || typeof(varCells) === "date") {
      var outFormula = String(varCells);
      if (replaceLineBreaks) {
        outFormula = outFormula.replace(/[\r\n]+/g, " ");
      }
      fo.WriteLine(getAddr(1 + rowOffset - 1, 1 + colOffset - 1) + ": " + outFormula);
    } else {
      try {
        for (var row = 1; row <= varCells.ubound(1); row++) {
          for (var col = 1; col <= varCells.ubound(2); col++) {
            try {
              var formula = varCells.getItem(row, col);
              if (typeof(formula) === "string" && formula.charAt(0) === "=") {
                if (replaceLineBreaks) {
                  formula = formula.replace(/[\r\n]+/g, " ");
                }
                fo.WriteLine(getAddr(row + rowOffset - 1, col + colOffset - 1) + ": " + formula);
              }
            } catch (e) {
              fo.WriteLine(getAddr(row + rowOffset - 1, col + colOffset - 1) + ": " + "Error" + e.number);
            }
          }
        }
      } catch (e) {
        fo.WriteLine(getAddr(rowOffset - 1, colOffset - 1) + ": " + "Error" + e.number);
      }
    }
  } catch (e) {
    fo.WriteLine("Error" + e.number + ": " + e.message);
  }
}

function getUsedRangeIncludingShapes(sht) {
  var col_max = 0;
  var row_max = 0;
  for (var it = new Enumerator(sht.Shapes); !it.atEnd(); it.moveNext()) {
    var shp = it.item();
    rng = shp.BottomRightCell;
    if (row_max < rng.Row + rng.Rows.Count - 1) { row_max = rng.Row + rng.Rows.Count - 1 }
    if (col_max < rng.Column + rng.Columns.Count - 1) { col_max = rng.Column + rng.Columns.Count - 1 }
  }
  var rng = sht.UsedRange;
  if (row_max < rng.Row + rng.Rows.Count - 1) { row_max = rng.Row + rng.Rows.Count - 1; }
  if (col_max < rng.Column + rng.Columns.Count - 1) { col_max = rng.Column + rng.Columns.Count - 1; }
  return sht.Range("A1", sht.Cells(row_max, col_max));
}

function ungroupShapes(sht) {
  try {
    var cnt;
    do {
      cnt = sht.Shapes.Count;
      for (var it = new Enumerator(sht.Shapes); !it.atEnd(); it.moveNext()) {
        try {
          var shp = it.item();
          if (shp.Type === pbGroup) {
            shp.Ungroup();
          }
        } catch (e) {}
      }
    } while (cnt !== sht.Shapes.Count);
  } catch (e) {}
}

function writeTextsInShapes(fo, sht) {
  try {
    for (var it = new Enumerator(sht.Shapes); !it.atEnd(); it.moveNext()) {
      try {
        var shp = it.item();
        fo.WriteLine(shp.Name + ": " + shp.TextFrame.Characters().Text);
      } catch (e) {}
    }
  } catch (e) {}
}

function writeHeadersAndFooters(fo, sht) {
  try {
    fo.WriteLine("LeftHeader: " + sht.PageSetup.LeftHeader);
    fo.WriteLine("CenterHeader: " + sht.PageSetup.CenterHeader);
    fo.WriteLine("RightHeader: " + sht.PageSetup.RightHeader);
    fo.WriteLine("LeftFooter: " + sht.PageSetup.LeftFooter);
    fo.WriteLine("CenterFooter: " + sht.PageSetup.CenterFooter);
    fo.WriteLine("RightFooter: " + sht.PageSetup.RightFooter);
  } catch (e) {}
}

function getModuleExtension(cmp) {
  switch (cmp.Type) {
  case 2:
    return ".cls";
  case 3:
    return ".frm";
  default:
    return ".bas";
  }
}

function sleep(sec) {
  if (sec == 0)
    return;
  wsh.run("ping.exe localhost -n " + sec, 0, true);
}

function saveRangeAsImage(sht, rng, filename) {
  var result = true;
  try {
    sht.Activate();
    var win = sht.Parent.Windows(1);
    win.DisplayGridlines = false;
    win.View = 1;

    var oldSheetsInNewWorkbook = sht.Application.SheetsInNewWorkbook;
    sht.Application.SheetsInNewWorkbook = 1;
    var wbkNew = sht.Application.Workbooks.Add();
    sht.Application.SheetsInNewWorkbook = oldSheetsInNewWorkbook;
    var shtNew = wbkNew.Sheets(1);
    var obj = wbkNew.Charts.Add();
    obj = obj.Location(2, shtNew.Name); // xlLocationAsObject=2
 
    if (sht.Application.Version <= 11) {
      obj.Parent.Width = rng.Width + 8;
      obj.Parent.Height = rng.Height + 8;
    } else {
      obj.Parent.Width = rng.Width;
      obj.Parent.Height = rng.Height;
    }
 
    for (var i = 0; i <= 1; i++) {
      if (sht.Application.Version < 14) {
        try {
          rng.CopyPicture(1, 2); // xlScreen=1, xlBitmap=2
          sleep(i);
          obj.Paste();
          obj.Export(filename, "PNG");
          result = true;
          break;
        } catch (e) {
          result = false;
          if (i == 0) {
            sleep(1);
          }
        }
      } else {
        try {
          rng.Copy();
          sleep(i);
          shtNew.Pictures.Paste();
          obj.Export(filename, "PNG");
          result = true;
          break;
        } catch (e) {
          result = false;
          if (i == 0) {
            sleep(1);
          }
        }
      }
    }
  } catch (e) {
    result = false;
  }
  wbkNew.Close(false);
  return result;
}

function getTimeStamp() {
  var dt = new Date();
  return dt.toLocaleString().split(" ")[1] + "." + ("000" + dt.getMilliseconds()).slice(-3);
}

function findRowByPosition(sht, rowBegin, rowEnd, pos) {
  var rowPrev, row, rowBeginOrg;
  row = rowEnd;
  rowBeginOrg = rowBegin;
  rowPrev = row;
  do {
    var height = sht.Range("A" + rowBeginOrg + ":A" + row).height;
    if (height < pos) {
      rowBegin = row;
    } else {
      rowEnd = row;
    }
    row = Math.floor((rowEnd - rowBegin) / 2) + rowBegin;
    if (row == rowPrev) {
      return row;
    }
    rowPrev = row;
  } while (true);
}

function findColumnByPosition(sht, columnBegin, columnEnd, pos) {
  var column = columnEnd;
  var columnBeginOrg = columnBegin;
  var columnPrev = column;
  do {
    var width = sht.Range(sht.Cells(1, columnBeginOrg), sht.Cells(1, column)).width;
    if (width < pos) {
      columnBegin = column;
    } else {
      columnEnd = column;
    }
    column = Math.floor((columnEnd - columnBegin) / 2) + columnBegin;
    if (column == columnPrev) {
      return column;
    }
    columnPrev = column;
  } while (true);
}


function saveSheetAsImage(sht, basefilename) {
  var imageWidth = regRead(REGKEY_PATH + "ImageWidth", 1000);
  var imageHeight = regRead(REGKEY_PATH + "ImageHeight", 3000);
  var rngUsed = getUsedRangeIncludingShapes(sht);
  var numX = 1;
  var column = rngUsed.Column;
  do {
    var columnEnd = findColumnByPosition(sht, column, rngUsed.Column + rngUsed.Columns.Count - 1, imageWidth);
    var numY = 1;
    var row = rngUsed.Row;
    do {
      var rowEnd = findRowByPosition(sht, row, rngUsed.Row + rngUsed.Rows.Count - 1, imageHeight);
      var rngImage = sht.Range(sht.Cells(row, column), sht.Cells(rowEnd, columnEnd));
      var filename = basefilename + "(" + numX + "-" + numY + ").png";
      if (!saveRangeAsImage(sht, rngImage, filename)) {
        var shtNew = sht.Parent.Sheets.Add();
        shtNew.Range("A1") = getTimeStamp() + ": Error" + Err.Number + ": " + Err.Description;
        shtNew.Columns.AutoFit();
        saveRangeAsImage(shtNew, shtNew.Range("A1"), filename);
        shtNew.Delete();
      }
      row = rowEnd + 1;
      numY = numY + 1;
    } while (row < rngUsed.row + rngUsed.Rows.Count - 1);
    column = columnEnd + 1;
    numX = numX + 1;
  } while (column < rngUsed.column + rngUsed.Columns.Count - 1);
}

function saveSheetAsHTML(sht, filename) {
  try {
    var pobj = sht.Parent.PublishObjects.Add(1, filename, sht.Name, "", 0, "CompareMSExcelFilesPlugin", ""); // 1 = xlSourceSheet, 0 = xlHtmlStatic
    pobj.Publish(true);
    pobj.AutoRepublish = false;
  } catch (e) {
    var fo = fso.CreateTextFile(filename, true, true);
    fo.Write(e.message);
    fo.Close();
    fo = null;
  }
}

function escape(sheetName) {
  var escape = sheetName.replace(/%/g, "%25").replace(/</g, "%3C").replace(/>/, "%3E").replace(/\|/, "%7C");
  switch (escape.toLowerCase().substr(0, 3)) {
  case "CON", "AUX", "NUL":
    if (escape.length == 3) {
      escape = "%" + escape;
    }
    break;
  case "COM", "LPT":
    if (escape.length == 4 && !isNaN(escape.substr(3, 1))) {
      escape = "%" + escape;
    }
    break;
  }
  return escape;
}

function get_PluginEvent() {
  return "FILE_FOLDER_PACK_UNPACK";
}

function get_PluginDescription() {
  return "Display MS Excel text content";
}

function get_PluginFileFilters() {
  return "\\.xls(\\..*)?$;\\.xlsx(\\..*)?$;\\.xlsm(\\..*)?$;\\.xlsb(\\..*)?;\\.xla(\\..*)?$;\\.xlax(\\..*)?$;\\.xltx(\\..*)?$;\\.xltm(\\..*)?$";
}

function get_PluginIsAutomatic() {
  return true;
}

function get_PluginUnpackedFileExtension() {
  return ".tsv";
}

function get_PluginExtendedProperties() {
  return "ProcessType=Content Extraction;FileType=MS-Excel;MenuCaption=MS-Excel";
}

function PluginOnEvent(eventType, obj) {
  mergeApp = obj;
}

function UnpackFile(fileSrc, fileDst, pbChanged, pSubcode) {
  var fo = fso.CreateTextFile(fileDst, true, true);
  var xl;
  try {
    xl = new ActiveXObject("Excel.Application");
  } catch (e) {}
  if (!xl) {
    throw new Error(30001, translate("${%1 is not installed.}").replace("%1", "Excel"));
  }
  xl.EnableEvents = false;
  xl.DisplayAlerts = false;

  var fileSrc2 = fileSrc;
  if (fso.GetExtensionName(fileSrc2) == "lnk") {
    fileSrc2 = wsh.CreateShortcut(fileSrc2).TargetPath;
    if (!fso.FileExists(fileSrc2)) {
      throw new Error(30001, "CompareMSExcelFiles.sct: " + fileSrc + ": Target file '" + fileSrc2 + "' not found");
    }
  }
  var wbk = xl.Workbooks.Open(fileSrc2, regRead(REGKEY_PATH + "UpdateLinks", 0), null, null, null, null, null, null, null, null, null, null,null, -1);
  if (regRead(REGKEY_PATH + "CompareDocumentProperties", false)) {
    fo.WriteLine("[Document Properties]");
    writeObjectProperties(fo, wbk.BuiltinDocumentProperties);
    fo.WriteLine("");
  }
 
  if (regRead(REGKEY_PATH + "CompareNames", true)) {
    fo.WriteLine("[Names]");
    writeObjectProperties(fo, wbk.Names);
    fo.WriteLine("");
  }
 
  for (var it = new Enumerator(wbk.Worksheets); !it.atEnd(); it.moveNext()) {
    var sht = it.item();
    if (regRead(REGKEY_PATH + "CompareCellValues", true)) {
      fo.WriteLine("[" + sht.Name + "]");
      writeCellValues(fo, sht);
      fo.WriteLine("");
    }
    if (regRead(REGKEY_PATH + "CompareFormulas", false)) {
      fo.WriteLine("[" + sht.Name + ".Formulas]");
      writeFormulas(fo, sht);
      fo.WriteLine("");
    }
    if (regRead(REGKEY_PATH + "CompareTextsInShapes", true)) {
      fo.WriteLine("[" + sht.Name + ".Shapes]");
      ungroupShapes(sht);
      writeTextsInShapes(fo, sht);
      fo.WriteLine("");
    }
    if (regRead(REGKEY_PATH + "CompareHeadersAndFooters", true)) {
      fo.WriteLine("[" + sht.Name + ".HeadersAndFooters]");
      writeHeadersAndFooters(fo, sht);
      fo.WriteLine("");
    }
  }
 
  if (regRead(REGKEY_PATH + "CompareVBAMacros", true)) {
    if (!isAccessibleVBAProject(wbk)) {
      fo.WriteLine(translate(MsgCannotGetMacros));
    } else {
      for (var it = new Enumerator(wbk.VBProject.VBComponents); !it.atEnd(); it.moveNext()) {
        var cmp = it.item();
        fo.WriteLine("[CodeModule." + cmp.Name + "]");
        if (cmp.CodeModule.CountOfLines > 0) {
          fo.WriteLine(cmp.CodeModule.Lines(1, cmp.CodeModule.CountOfLines));
        }
        fo.WriteLine("");
      }
    }
  }
  sht = null;

  wbk.Saved = true;
  wbk.Close();
  wbk = null;
  xl.Quit();
  xl = null;
  fo.Close();
  fo = null;

  pbChanged = true;
  pSubcode = 0;
  
  CollectGarbage();

  var result = new ActiveXObject("Scripting.Dictionary");
  result.Add(0, true);
  result.Add(1, pbChanged);
  result.Add(2, pSubcode);
  return result.Items();
}

function PackFile(fileSrc, fileDst, pbChanged, pSubcode) {
  return false;
}

function IsFolder(file) {
  return regRead(REGKEY_PATH + "UnpackToFolder", false);
}

function UnpackFolder(fileSrc, folderDst, pbChanged, pSubcode) {
  if (!fso.FolderExists(folderDst)) { fso.CreateFolder(folderDst); }
  var xl;
  try {
    xl = new ActiveXObject("Excel.Application");
  } catch (e) {}
  if (!xl) {
    throw new Error(30001, translate("${%1 is not installed.}").replace("%1", "Excel"));
  }
  xl.EnableEvents = false;
  xl.DisplayAlerts = false;

  var backgroundChecking = xl.ErrorCheckingOptions.BackgroundChecking;
  xl.ErrorCheckingOptions.BackgroundChecking = false;

  var fileSrc2 = fileSrc;
  if (fso.GetExtensionName(fileSrc2) === "lnk") {
    fileSrc2 = wsh.CreateShortcut(fileSrc2).TargetPath;
    if (!fso.FileExists(fileSrc2)) {
      throw new Error(30001, "CompareMSExcelFiles.sct: " + fileSrc + ": Target file '" + fileSrc2 + "' not found");
    }
  }
  var wbk = xl.Workbooks.Open(fileSrc2, regRead(REGKEY_PATH + "UpdateLinks", 0), null, null, null, null, null, null, null, null, null, null, null, -1);

  digit = String(wbk.Worksheets.Count).length;
  if (digit <= 0) { digit = 1; }
  var zf = Array(digit + 1).join("0");

  var includeSheetOrderNumber = regRead(REGKEY_PATH + "IncludeSheetOrderNumber", true);
  var prefixForGlobal = includeSheetOrderNumber ? "(" + zf + ")" : "";

  if (regRead(REGKEY_PATH + "CompareDocumentProperties", false)) {
    var fo = fso.CreateTextFile(fso.BuildPath(folderDst, prefixForGlobal + "DocumentProperties.txt"), true, true);
    writeObjectProperties(fo, wbk.BuiltinDocumentProperties);
    fo.Close();
    fo = null;
  }

  if (regRead(REGKEY_PATH + "CompareNames", true)) {
    var fo = fso.CreateTextFile(fso.BuildPath(folderDst, prefixForGlobal + "Names.txt"), true, true);
    writeObjectProperties(fo, wbk.Names);
    fo.Close();
    fo = null;
  }

  var No = 1;
  for (var it = new Enumerator(wbk.Worksheets); !it.atEnd(); it.moveNext()) {
    var sht = it.item();
    No = zf + No;
    No = No.substr(No.length - digit, digit);
    var prefix = includeSheetOrderNumber ? "(" + No + ")" : "";
    if (regRead(REGKEY_PATH + "CompareCellValues", true)) {
      var fo = fso.CreateTextFile(fso.BuildPath(folderDst, prefix + escape(sht.Name) + ".tsv"), true, true);
      writeCellValues(fo, sht);
      fo.Close();
      fo = null;
    }
 
    if (regRead(REGKEY_PATH + "CompareFormulas", false)) {
      var fo = fso.CreateTextFile(fso.BuildPath(folderDst, prefix + escape(sht.Name) + "_Formulas.txt"), true, true);
      writeFormulas(fo, sht);
      fo.Close();
      fo = null;
    }
 
    if (regRead(REGKEY_PATH + "CompareTextsInShapes", true)) {
      var fo = fso.CreateTextFile(fso.BuildPath(folderDst, prefix + escape(sht.Name) + "_Shapes.txt"), true, true);
      ungroupShapes(sht);
      writeTextsInShapes(fo, sht);
      fo.Close();
      fo = null;
    }
 
    if (regRead(REGKEY_PATH + "CompareHeadersAndFooters", true)) {
      var fo = fso.CreateTextFile(fso.BuildPath(folderDst, prefix + escape(sht.Name) + "_HeadersAndFooters.txt"), true, true);
      writeHeadersAndFooters(fo, sht);
      fo.Close();
      fo = null;
    }
 
    if (regRead(REGKEY_PATH + "CompareWorksheetsAsImage", true)) {
      saveSheetAsImage(sht, fso.BuildPath(folderDst, prefix + escape(sht.Name)));
    }
 
    if (regRead(REGKEY_PATH + "CompareWorksheetsAsHTML", false)) {
      saveSheetAsHTML(sht, fso.BuildPath(folderDst, prefix + escape(sht.Name) + ".html"));
    }
    No = parseInt(No, 10) + 1;
    sht = null;
  }
  if (regRead(REGKEY_PATH + "CompareVBAMacros", true)) {
    if (!isAccessibleVBAProject(wbk)) {
      var fo = fso.CreateTextFile(fso.BuildPath(folderDst, "CannotGetMacros.bas"), true, true);
      fo.WriteLine(translate(MsgCannotGetMacros));
      fo.Close();
      fo = null;
    } else {
      for (var it = new Enumerator(wbk.VBProject.VBComponents); !it.atEnd(); it.moveNext()) {
        var cmp = it.item();
        cmp.Export(fso.BuildPath(folderDst, cmp.Name + getModuleExtension(cmp)));
      }
    }
  }

  wbk.Saved = true;
  wbk.Close();
  wbk = null;
  xl.ErrorCheckingOptions.BackgroundChecking = backgroundChecking;
  xl.Quit();
  xl = null;

  pbChanged = true;
  pSubcode = 0;
  
  CollectGarbage();

  var result = new ActiveXObject("Scripting.Dictionary");
  result.Add(0, true);
  result.Add(1, pbChanged);
  result.Add(2, pSubcode);
  return result.Items();
}

function PackFolder(folderSrc, fileDst, pbChanged, pSubcode) {
  return false;
}

function translate(text) {
  var re = /\${([^}]+)}/g;
  var matches;
  while ((matches = re.exec(text)) != null) {
    text = text.replace(matches[0], mergeApp.Translate(matches[1]));
  }
  return text;
}

function ShowSettingsDialog() {
  var tname = fso.BuildPath(fso.GetSpecialFolder(2), fso.GetTempName() + ".hta");
  var xmlfile = fso.BuildPath(fso.GetSpecialFolder(2), fso.GetTempName() + ".xml");
  var tfile = fso.CreateTextFile(tname, true, true);
  tfile.Write(translate(getResource("dialog1")));
  tfile.Close();
  exportSettingsToXMLFile(xmlfile);
  var mshta = wsh.ExpandEnvironmentStrings("%SystemRoot%\\System32\\mshta.exe");
  if (!fso.FileExists(mshta)) {
    mshta = wsh.ExpandEnvironmentStrings("%SystemRoot%\\SysWOW64\\mshta.exe");
  }
  run(wsh, "\"" + mshta + "\" \"" + tname + "\"  \"" + xmlfile + "\"");
  importSettingsFromXMLFile(xmlfile);
  fso.DeleteFile(tname);
  fso.DeleteFile(xmlfile);
}

function run(sh, cmd) {
  sh.Run(cmd, 1, true);
}

function exportSettingsToXMLFile(filepath) {
  var key_defvalues = {
    "UnpackToFolder" : false,
    "IncludeSheetOrderNumber" : true,
    "UpdateLinks" : false,
    "CompareDocumentProperties" : false,
    "CompareNames" : true,
    "CompareCellValues" : true,
    "CompareWorksheetsAsImage" : true,
    "CompareWorksheetsAsHTML" : false,
    "ImageWidth" : 1000,
    "ImageHeight" : 3000,
    "CompareFormulas" : false,
    "CompareFormulasReplaceLineBreaks" : false, 
    "CompareTextsInShapes" : true,
    "CompareHeadersAndFooters" : true,
    "CompareVBAMacros" : true
  };
  var doc = new ActiveXObject("MSXML2.DOMDocument");
  var fso = new ActiveXObject("Scripting.FileSystemObject");
  var ts = fso.OpenTextFile(filepath, 2, true, -1);
  var root = doc.createElement("properties");
  for (var key in key_defvalues) {
    var el = doc.createElement("property");
    var val = regRead(REGKEY_PATH + key, key_defvalues[key]);
    var cdata = doc.createCDATASection(val);
    el.appendChild(cdata);
    el.setAttribute("name", REGKEY_PATH + key);
    el.setAttribute("type", typeof val);
    root.appendChild(el);
  }
  doc.appendChild(root);
  ts.Write(doc.xml);
  ts.Close();
}

function importSettingsFromXMLFile(filepath) {
  var fso = new ActiveXObject("Scripting.FileSystemObject");
  var ts = fso.OpenTextFile(filepath, 1, true, -1);
  var xml = ts.ReadAll();
  var doc = new ActiveXObject("MSXML2.DOMDocument");
  doc.async = false;
  doc.loadXML(xml);
  ts.Close();
  var nodes = doc.documentElement.childNodes;
  for (var i = 0; i < nodes.length; i++) {
    regWrite(nodes[i].getAttribute("name"), nodes[i].text, (nodes[i].getAttribute("type") === "string") ? "REG_SZ" : "REG_DWORD");
  }
}

</script>

<resource id="dialog1">
<![CDATA[
<!DOCTYPE html>
<html>
  <head>
    <HTA:APPLICATION ID="objHTA">
    <title>${CompareMSExcelFiles.sct WinMerge Plugin Options}</title>
    <meta content="text/html" charset="UTF-16">
    <style>
    body { background-color: #f2f2f2; font-family: Arial, sans-serif; }
    .container { margin: 2em; }
    ul { list-style-type: none; margin: 0; padding: 0; }
    li ul li { padding-left: 2em }
    .btn-container { margin-top: 1.5em; text-align: right; }
    input[type="button"] { border: none; padding: 0.6em 2em; height: 2.5em; text-align: center; }
    .btn-ok { color: #fff; background-color: #05c; }
    .btn-ok:hover { background-color: #04b; }
    .btn-cancel { color: #333; background-color: #ddd; }
    .btn-cancel:hover { background-color: #ccc; }
    </style>
    <script type="text/javascript">
      var REGKEY_PATH = "Plugins\\CompareMSExcelFiles.sct/";
      var xmlFilePath;
      var settings = {};

      function regRead(key, defaultValue) {
        return settings.hasOwnProperty(key) ? settings[key] : defaultValue;
      }

      function regWrite(key, value, type) {
        settings[key] = (type === "REG_DWORD") ? Number(value) : String(value);
      }

      function loadSettingsFromXMLFile(filepath) {
        var fso = new ActiveXObject("Scripting.FileSystemObject");
        var ts = fso.OpenTextFile(filepath, 1, true, -1);
        var xml = ts.ReadAll();
        var doc = new ActiveXObject("MSXML2.DOMDocument");
        doc.async = false;
        doc.loadXML(xml);
        ts.Close();
        var nodes = doc.documentElement.childNodes;
        for (var i = 0; i < nodes.length; i++) {
          regWrite(nodes[i].getAttribute("name"), nodes[i].text, (nodes[i].getAttribute("type") === "string") ? "REG_SZ" : "REG_DWORD");
        }
        return settings;
      }

      function saveSettingsToXMLFile(filepath, settings) {
        var doc = new ActiveXObject("MSXML2.DOMDocument");
        var fso = new ActiveXObject("Scripting.FileSystemObject");
        var ts = fso.OpenTextFile(filepath, 2, true, -1);
        var root = doc.createElement("properties");
        for (var key in settings) {
          if (settings.hasOwnProperty(key)) {
            var el = doc.createElement("property");
            var val = settings[key];
            var cdata = doc.createCDATASection(val);
            el.appendChild(cdata);
            el.setAttribute("name", key);
            el.setAttribute("type", typeof val);
            root.appendChild(el);
          }
        }
        doc.appendChild(root);
        ts.Write(doc.xml);
        ts.Close();
      }

      function onload() {
        xmlFilePath = objHTA.commandLine.split('"')[3];
        settings = loadSettingsFromXMLFile(xmlFilePath);

        var dpi = window.screen.deviceXDPI;
        var w = 600 * dpi / 96, h = 560 * dpi / 96;
        window.resizeTo(w, h);
        window.moveTo((screen.width - w) / 2, (screen.height - h) / 2);

        chkUnpackToFolder.checked = regRead(REGKEY_PATH + "UnpackToFolder", false);
        chkIncludeSheetOrderNumber.checked = regRead(REGKEY_PATH + "IncludeSheetOrderNumber", true);
        chkUpdateLinks.checked = (regRead(REGKEY_PATH + "UpdateLinks", 0) == 3);
        chkCompareDocumentProperties.checked = regRead(REGKEY_PATH + "CompareDocumentProperties", false);
        chkCompareNames.checked = regRead(REGKEY_PATH + "CompareNames", true);
        chkCompareCellValues.checked = regRead(REGKEY_PATH + "CompareCellValues", true);
        chkCompareWorksheetsAsImage.checked = regRead(REGKEY_PATH + "CompareWorksheetsAsImage", true);
        chkCompareWorksheetsAsHTML.checked = regRead(REGKEY_PATH + "CompareWorksheetsAsHTML", false);
        txtImageWidth.value = regRead(REGKEY_PATH + "ImageWidth", 1000);
        txtImageHeight.value = regRead(REGKEY_PATH + "ImageHeight", 3000);
        chkCompareFormulas.checked = regRead(REGKEY_PATH + "CompareFormulas", false);
        chkCompareFormulasBrks.checked = regRead(REGKEY_PATH + "CompareFormulasReplaceLineBreaks", false);
        chkCompareTextsInShapes.checked = regRead(REGKEY_PATH + "CompareTextsInShapes", true);
        chkCompareHeadersAndFooters.checked = regRead(REGKEY_PATH + "CompareHeadersAndFooters", true);
        chkCompareVBAMacros.checked = regRead(REGKEY_PATH + "CompareVBAMacros", true);
        chkUnpackToFolder_onclick();
        chkCompareWorksheetsAsImage_onclick();
        chkCompareWorksheetsAsHTML_onclick();
        document.onkeydown = onkeydown;
      }

      function onkeydown() {
        var k = event.keyCode;
        if (k == 13/*Enter*/) {
          btnOk_onclick();
        } else if (k == 27/*Escape*/) {
          btnCancel_onclick();
        }
      }

      function chkUnpackToFolder_onclick() {
        if (!chkUnpackToFolder.checked) {
          chkCompareWorksheetsAsImage.checked = false;
          chkCompareWorksheetsAsHTML.checked = false;
        }
      }

      function chkCompareWorksheetsAsImage_onclick() {
        if (chkCompareWorksheetsAsImage.checked)
          chkUnpackToFolder.checked = true;
      }

      function chkCompareWorksheetsAsHTML_onclick() {
        if (chkCompareWorksheetsAsHTML.checked)
          chkUnpackToFolder.checked = true;
      }
	  
      function btnOk_onclick() {
        regWrite(REGKEY_PATH + "UnpackToFolder", chkUnpackToFolder.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "IncludeSheetOrderNumber", chkIncludeSheetOrderNumber.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "UpdateLinks", chkUpdateLinks.checked ? 3 : 0, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareDocumentProperties", chkCompareDocumentProperties.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareNames", chkCompareNames.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareCellValues", chkCompareCellValues.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareWorksheetsAsImage", chkCompareWorksheetsAsImage.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareWorksheetsAsHTML", chkCompareWorksheetsAsHTML.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "ImageWidth", Number(txtImageWidth.value), "REG_DWORD");
        regWrite(REGKEY_PATH + "ImageHeight", Number(txtImageHeight.value), "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareFormulas", chkCompareFormulas.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareFormulasReplaceLineBreaks", chkCompareFormulasBrks.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareTextsInShapes", chkCompareTextsInShapes.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareHeadersAndFooters", chkCompareHeadersAndFooters.checked, "REG_DWORD");
        regWrite(REGKEY_PATH + "CompareVBAMacros", chkCompareVBAMacros.checked, "REG_DWORD");

        saveSettingsToXMLFile(xmlFilePath, settings);

        window.close();
      }

      function btnCancel_onclick() {
        saveSettingsToXMLFile(xmlFilePath, {});

        window.close();
      }

      function onlyNumeric() {
        var ev = event;
        var k = ev.keyCode;

        if (ev.shiftKey) {
          if (k == 37 || k == 39) {
            return true;
          }
        } else if (ev.altKey) {
          return true;
        } else if (ev.ctrlKey) {
          return true;
        } else if (k == 8 || k == 9 || k == 37 || k == 39 || k == 46 /* || k == 144 */
            || (48 <= k && k <= 57) || (96 <= k && k <= 105)) {
          return true;
        }

        ev.returnValue = false;
        return false;
      }

      function onPaste() {
        var ev = event;
        var paste = (ev.clipboardData || window.clipboardData).getData("text");
        if (!paste.match(/^[0-9]{0,4}$/)) {
          paste = "";
          if (ev.preventDefault) {
              ev.preventDefault();
          }
          ev.returnValue = false;
          return false;
        } else {
          return true;
        }
      }
    </script>
  </head>
  <body onload="onload();">
    <div class="container">
      <ul>
        <li>
          <input id="chkUnpackToFolder" type="checkbox" onclick="chkUnpackToFolder_onclick();"/>
          <label for="chkUnpackToFolder">${Extract workbook data to multiple files}</label>
          <ul>
            <li>
              <input id="chkIncludeSheetOrderNumber" type="checkbox" />
              <label for="chkIncludeSheetOrderNumber">${Include sheet order number in filename}</label>
            </li>
          </ul>
        </li>
        <li>
          <input id="chkUpdateLinks" type="checkbox" />
          <label for="chkUpdateLinks">${Update external references (links)}</label>
        </li>
        <li>
          <input id="chkCompareDocumentProperties" type="checkbox" />
          <label for="chkCompareDocumentProperties">${Compare document properties}</label>
        </li>
        <li>
          <input id="chkCompareNames" type="checkbox" />
          <label for="chkCompareNames">${Compare names}</label>
        </li>
        <li>
          <input id="chkCompareCellValues" type="checkbox" />
          <label for="chkCompareCellValues">${Compare cell values}</label>
        </li>
        <li>
          <input id="chkCompareWorksheetsAsImage" type="checkbox" onclick="chkCompareWorksheetsAsImage_onclick();"/>
          <label for="chkCompareWorksheetsAsImage">${Compare worksheets as image (very slow)}</label>
          <ul>
            <li>
              <label>${ - Image split size: }</label>
              <input id="txtImageWidth"  type="text" size="5" maxlength="4" onkeyDown="return onlyNumeric();" onpaste="return onPaste();" />
              <span> x </span>
              <input id="txtImageHeight" type="text" size="5" maxlength="4" onkeyDown="return onlyNumeric();" onpaste="return onPaste();" />
            </li>
          </ul>
        </li>
        <li>
          <input id="chkCompareWorksheetsAsHTML" type="checkbox" onclick="chkCompareWorksheetsAsHTML_onclick();"/>
          <label for="chkCompareWorksheetsAsHTML">${Compare worksheets as HTML}</label>
        </li>
        <li>
          <input id="chkCompareFormulas" type="checkbox" />
          <label for="chkCompareFormulas">${Compare formulas}</label>
          <ul>
            <li>
              <input id="chkCompareFormulasBrks" type="checkbox" />
              <label for="chkCompareFormulasBrks">${Replace line breaks in formulas with spaces}</label>
            </li>
          </ul>
        </li>
        <li>
          <input id="chkCompareTextsInShapes" type="checkbox" />
          <label for="chkCompareTextsInShapes">${Compare texts in shapes}</label>
        </li>
        <li>
          <input id="chkCompareHeadersAndFooters" type="checkbox" />
          <label for="chkCompareHeadersAndFooters">${Compare headers and footers}</label>
        </li>
        <li>
          <input id="chkCompareVBAMacros" type="checkbox" />
          <label for="chkCompareVBAMacros">${Compare VBA macros}</label>
        </li>
      </ul>
      <div class="btn-container">
        <input type="button" class="btn-ok" onclick="btnOk_onclick();" value="${OK}" />
        <input type="button" class="btn-cancel" onclick="btnCancel_onclick();" value="${Cancel}" />
      </div>
    </div>
  </body>
</html>
]]>
</resource>

</scriptlet>
